package handler

import (
	"fmt"
	"github.com/ahmetb/go-linq/v3"
	"github.com/gin-gonic/gin"
	"github.com/xuri/excelize/v2"
	"lib/http"
	"lib/id"
	"lib/structs/enum"
	"lib/structs/lms"
	"lib/structs/tables"
	"lib/utils"
	"lms/global"
	"lms/repository"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

func (a questionApi) importQuestions(c *gin.Context) (err error) {
	self := http.GetCurrentUser(c)
	f, err := c.FormFile("file")
	if err != nil {
		http.BadWithParamsError(c)
		return
	}
	filename := fmt.Sprintf("%d_%s", id.GetSnowId(), f.Filename)
	err = c.SaveUploadedFile(f, filepath.Join(enum.FilePath.TempFolder, filename))
	if err != nil {
		http.BadWithCode(c, enum.ErrorMap.CoursewareFileUploadFail)
		return
	}
	xls, err := excelize.OpenFile(filepath.Join(enum.FilePath.TempFolder, filename))
	if err != nil {
		http.BadWithCode(c, enum.ErrorMap.ResolveQuestionExcelError)
		return
	}
	defer xls.Close()
	rows, err := xls.GetRows("Sheet1")
	if err != nil {
		http.BadWithCode(c, enum.ErrorMap.ResolveQuestionExcelError)
		return
	}
	// 先把目录传创建出来
	var questionFolderList = make([]string, 0)
	for i, v := range rows {
		if i == 0 {
			continue
		}
		if v[0] == "" {
			continue
		}
		questionFolderList = append(questionFolderList, strings.TrimSpace(v[2]))
	}
	err = a.handleQuestionFolder(questionFolderList, self.UserID, self.Nickname)
	if err != nil {
		http.BadWithDB(c, err)
		return
	}
	depthMap := a.getQuestionFolderDepthMap()
	var li = make([]*tables.Question, 0)
	for i, v := range rows {
		if i == 0 {
			continue
		}
		if v[0] == "" {
			continue
		}
		q := &tables.Question{
			Id:             id.GetSnowId(),
			Kind:           a.getQuestionKind(strings.TrimSpace(v[1])),
			Title:          strings.TrimSpace(v[0]),
			SubTitle:       "",
			InputFun:       enum.QuestionInputType.XLSX,
			Score:          utils.StrToFloat(strings.TrimSpace(v[7])),
			Options:        strings.TrimSpace(v[3]),
			Answer:         strings.TrimSpace(v[4]),
			ScoringRule:    utils.StrToInt(strings.TrimSpace(v[8])),
			PartScore:      utils.StrToFloat(strings.TrimSpace(v[9])),
			HardLv:         utils.StrToInt(strings.TrimSpace(v[5])),
			Label:          strings.TrimSpace(v[6]),
			FolderId:       depthMap[strings.TrimSpace(v[2])].FolderId,
			AccessControl:  utils.StrToInt(strings.TrimSpace(v[10])),
			AutoCreateInfo: tables.AutoCreateInfo{},
		}
		if q.Kind == enum.QuestionKind.FillInTheBlank {
			q.Title = a.fillTheBlanksGetTitle(q.Title)
			q.Answer = a.fillTheBlanksGetResult(q.Title)
		}
		li = append(li, q)
	}
	return global.DB.Model(&tables.Question{}).CreateInBatches(li, len(li)).Error
}

// handleQuestionFolder 批量处理题库目录,
func (a questionApi) handleQuestionFolder(in []string, uid int64, uname string) (err error) {
	var li []string
	linq.From(in).Distinct().ToSlice(&li)
	for _, v := range li {
		err = a.createQuestionFolder(v, uid, uname)
		if err != nil {
			return err
		}
	}
	return nil
}

// fillTheBlanksGetResult 将填空题中的答案去掉，同时将 {} 替换为 (__)
func (a questionApi) fillTheBlanksGetTitle(title string) string {
	regex := regexp.MustCompile(`\{(.*?)\}`)
	matches := regex.FindAllStringSubmatch(title, -1)
	for _, match := range matches {
		if len(match) > 1 {
			strings.ReplaceAll(title, fmt.Sprintf("{%s}", match[1]), "(__)")
		}
	}
	return title
}

// fillTheBlanksGetResult 提取填空题中的答案部分，多个答案的使用 | 分隔
func (a questionApi) fillTheBlanksGetResult(title string) (rStr string) {
	regex := regexp.MustCompile(`\{(.*?)\}`)
	matches := regex.FindAllStringSubmatch(title, -1)
	for _, match := range matches {
		if len(match) > 1 {
			rStr += match[1] + "|"
		}
	}
	return rStr[:len(rStr)-1]
}

// getQuestionKind 格式化题目类型
func (a questionApi) getQuestionKind(sKind string) (kind int) {
	switch sKind {
	case "单选题":
		kind = enum.QuestionKind.SingleChoose
	case "多选题":
		kind = enum.QuestionKind.MultipleChoice
	case "判断题":
		kind = enum.QuestionKind.Switch
	case "填空题":
		kind = enum.QuestionKind.FillInTheBlank
	case "主观题":
		kind = enum.QuestionKind.Subjective
	default:
		kind = 0
	}
	return
}

// getQuestionFolderTree 递归获取题目目录树
func (a questionApi) getQuestionFolderTree(all []*lms.QuestionFolderTree, parentId int64) []*lms.QuestionFolderTree {
	subs := make([]*lms.QuestionFolderTree, 0)
	for _, v := range all {
		if v.ParentId == parentId {
			v.Children = a.getQuestionFolderTree(all, v.FolderId)
			subs = append(subs, v)
		}
	}
	return subs
}

// createQuestionFolder 创建题目目录，如果父级目录不存在会先创建父级目录
func (a questionApi) createQuestionFolder(depth string, uid int64, uname string) error {
	// 路径统一使用 / ，因为\存在转义问题，而且中文下对应、.
	depth = strings.ReplaceAll(depth, "\\", "/")
	depths := strings.Split(depth, "/")
	var tempDept string
	for i := 1; i <= len(depths); i++ {
		tempDept = path.Join(depths[:i]...)
		tempDept = strings.ReplaceAll(tempDept, "\\", "/")
		var m = new(tables.QuestionFolder)
		err := global.DB.Model(&tables.QuestionFolder{}).Where(tables.QuestionFolder{PathDepth: tempDept}).Find(m).Error
		if err != nil {
			return err
		}
		var parentId int64
		if i > 1 {
			parentDept := path.Join(depths[:i-1]...)
			var parentFolder tables.QuestionFolder
			err = global.DB.Model(&tables.QuestionFolder{}).Where(tables.QuestionFolder{PathDepth: parentDept}).Find(&parentFolder).Error
			if err != nil {
				return err
			}
			parentId = parentFolder.FolderId
		}
		if m.FolderId == 0 {
			m = &tables.QuestionFolder{
				FolderId:  id.GetSnowId(),
				Name:      depths[i-1],
				PathDepth: tempDept,
				Describe:  "导入时自动创建",
				ParentId:  parentId,
				Sort:      0,
				AutoCreateInfo: tables.AutoCreateInfo{
					Creator:   uname,
					CreatorId: uid,
					CreatedAt: 0,
					UpdatedAt: 0,
					DeletedAt: 0,
				},
			}
			err = global.DB.Save(m).Error
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// getQuestionFolderDepthMap 获取题库目录列表，将其转换为map，key为路径深度
func (a questionApi) getQuestionFolderDepthMap() (depthMap map[string]tables.QuestionFolder) {
	depthMap = make(map[string]tables.QuestionFolder, 0)
	li, err := repository.ExamDB.FindQuestionFolderAll()
	if err != nil {
		return
	}
	for _, v := range li {
		depthMap[v.PathDepth] = v
	}
	return depthMap
}
